/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is Forte for Java, Community Edition. The Initial
* Developer of the Original Code is Sun Microsystems, Inc. Portions
* Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
*/
package org.openide.src.nodes;
import java.awt.Component;
import java.awt.datatransfer.Transferable;
import java.beans.*;
import java.io.IOException;
import java.lang.reflect.Modifier;
import java.lang.reflect.InvocationTargetException;
import java.util.ResourceBundle;
import java.util.Set;
import org.openide.*;
import org.openide.src.*;
import org.openide.nodes.*;
import org.openide.util.NbBundle;
import org.openide.util.Utilities;
import org.openide.util.datatransfer.NewType;
import org.openide.util.datatransfer.PasteType;
/** This class defines utilities for editing source using hierarchy API,
* e.g. creation new types for class elements, runAtomicAsUser support, ...
*
* @author Petr Hamernik
*/
class SourceEditSupport {
static final ResourceBundle bundle = NbBundle.getBundle(SourceEditSupport.class);
static final String[] MENU_NAMES = {
bundle.getString("MENU_CREATE_BLOCK"), bundle.getString("MENU_CREATE_VARIABLE"),
bundle.getString("MENU_CREATE_CONSTRUCTOR"), bundle.getString("MENU_CREATE_METHOD"),
bundle.getString("MENU_CREATE_CLASS"), bundle.getString("MENU_CREATE_INTERFACE")
};
/* Get the new types that can be created in this node.
* For example, a node representing a Java package will permit classes to be added.
* @return array of new type operations that are allowed
*/
public static NewType[] createNewTypes(ClassElement element) {
if (element.isClass()) {
// class new types
return new NewType[] {
new ElementNewType(element, (byte) 0),
new ElementNewType(element, (byte) 1),
new ElementNewType(element, (byte) 2),
new ElementNewType(element, (byte) 3),
new ElementNewType(element, (byte) 4),
new ElementNewType(element, (byte) 5)
};
}
else {
// interface new types
return new NewType[] {
new ElementNewType(element, (byte) 1),
new ElementNewType(element, (byte) 3),
new ElementNewType(element, (byte) 4),
new ElementNewType(element, (byte) 5)
};
}
}
/** New types for class element */
static class ElementNewType extends NewType {
/** Class element where to create new element */
ClassElement element;
/** The kind of element to create */
byte kind;
/** Creates new type
* @param element Where to create new element.
* @param kind The kind of the element to create
*/
public ElementNewType(ClassElement element, byte kind) {
this.element = element;
this.kind = kind;
}
/** Get the name of the new type.
* @return localized name.
*/
public String getName() {
return MENU_NAMES[kind];
}
/** Help context */
public org.openide.util.HelpCtx getHelpCtx() {
return new org.openide.util.HelpCtx (SourceEditSupport.class.getName () + ".newElement" + kind); // NOI18N
}
/** Creates new element */
public void create () throws IOException {
final Identifier outerName = element.getName();
final boolean outerIsClass = element.isClass();
Element newElement = null;
try {
switch (kind) {
case 0:
{
// Adding initializer
InitializerElement e = new InitializerElement();
e.setStatic(true);
e.setBody("\n"); // NOI18N
newElement = e;
break;
}
case 1:
{
// Adding field
FieldElement e = new FieldElement();
e.setType(Type.INT);
e.setName(Identifier.create("newField")); // NOI18N
e.setModifiers(Modifier.PUBLIC + (outerIsClass ? 0 : Modifier.STATIC));
if (openCustomizer(new FieldCustomizer(e), "TIT_NewField")) // NOI18N
newElement = e;
break;
}
case 2:
{
// Adding constructor
ConstructorElement e = new ConstructorElement();
e.setName(Identifier.create(((ClassElement)element).getName().getName()));
e.setModifiers(Modifier.PUBLIC);
e.setBody("\n"); // NOI18N
if (openCustomizer(new MethodCustomizer(e), "TIT_NewConstructor")) // NOI18N
newElement = e;
break;
}
case 3:
{
// Adding method
MethodElement e = new MethodElement();
e.setReturn(Type.VOID);
e.setName(Identifier.create("newMethod")); // NOI18N
e.setModifiers(Modifier.PUBLIC);
e.setBody(outerIsClass ? "\n" : null); // NOI18N
if (openCustomizer(new MethodCustomizer(e), "TIT_NewMethod")) // NOI18N
newElement = e;
break;
}
case 4:
{
// Adding inner class
ClassElement e = new ClassElement();
e.setName(Identifier.create(outerName.getFullName() + ".InnerClass", "InnerClass")); // NOI18N
e.setModifiers(Modifier.PUBLIC);
e.setClassOrInterface(true);
if (openCustomizer(new ClassCustomizer(e), "TIT_NewClass")) // NOI18N
newElement = e;
break;
}
case 5:
{
// Adding inner interface
ClassElement e = new ClassElement();
e.setName(Identifier.create(outerName.getFullName() + ".InnerInterface", "InnerInterface")); // NOI18N
e.setModifiers(Modifier.PUBLIC);
e.setClassOrInterface(false);
if (openCustomizer(new ClassCustomizer(e), "TIT_NewInterface")) // NOI18N
newElement = e;
break;
}
}
}
catch (SourceException exc) {
// shouldn't happen - memory implementation
// is not based on java source.
}
if (newElement == null)
return;
final Element addingElement = newElement;
SourceEditSupport.invokeAtomicAsUser(element, new SourceEditSupport.ExceptionalRunnable() {
public void run() throws SourceException {
switch (kind) {
case 0:
((ClassElement)element).addInitializer((InitializerElement)addingElement);
return;
case 1:
((ClassElement)element).addField((FieldElement)addingElement);
return;
case 2:
((ClassElement)element).addConstructor((ConstructorElement)addingElement);
return;
case 3:
((ClassElement)element).addMethod((MethodElement)addingElement);
return;
case 4:
case 5:
element.addClass((ClassElement)addingElement);
return;
}
}
});
}
}
/** Show dialog and allow user to modify new element.
* @param customizer The component to be displayed
* @param titleKey the key to resource bundle for the title of dialog
* @return <CODE>true</CODE> if user pressed OK button,
* otherwise <CODE>false</CODE> (for CANCEL)
*/
static boolean openCustomizer(Component customizer, String titleKey) {
NotifyDescriptor desriptor = new NotifyDescriptor(
customizer,
ElementNode.bundle.getString(titleKey),
NotifyDescriptor.OK_CANCEL_OPTION,
NotifyDescriptor.PLAIN_MESSAGE,
null, null);
Object ret = TopManager.getDefault().notify(desriptor);
return (ret == NotifyDescriptor.OK_OPTION);
}
/** Invokes the runnable using NbDocument.runAtomicAsUser.
* @exception IOException If SourceException occured inside the runnable.
*/
static void invokeAtomicAsUser(Element element, final ExceptionalRunnable exRun) throws IOException {
final SourceException[] ex = { null };
try {
SourceElement source = SourceEditSupport.findSource(element);
Runnable run = new Runnable() {
public void run() {
try {
exRun.run();
}
catch (SourceException e) {
ex[0] = e;
}
}
};
source.runAtomicAsUser(run);
}
catch (SourceException e) {
ex[0] = e;
}
if (ex[0] != null) {
throw new IOException(ex[0].getMessage());
}
}
/** This interface is used like runnable, but its method run
* could throw BadLocationException.
* @exception SourceException
*/
static interface ExceptionalRunnable {
public void run() throws SourceException;
}
/** Find the source for the specifier element.
* @exception SourceException if SourceElement cannot be found
*/
static SourceElement findSource(Element element) throws SourceException {
SourceElement source = null;
ClassElement clazz = null;
if (element instanceof ClassElement) {
clazz = (ClassElement) element;
}
else if (element instanceof MemberElement) {
clazz = ((MemberElement) element).getDeclaringClass();
}
else if (element instanceof InitializerElement) {
clazz = ((InitializerElement) element).getDeclaringClass();
}
else if (element instanceof SourceElement) {
return (SourceElement) element;
}
if (clazz != null) {
source = clazz.getSource();
if (source != null)
return source;
}
throw new SourceException();
}
}
/*
* Log
* 9 Gandalf 1.8 1/12/00 Petr Hamernik i18n using perl script
* (//NOI18N comments added)
* 8 Gandalf 1.7 1/11/00 Jesse Glick Context help.
* 7 Gandalf 1.6 1/5/00 Jaroslav Tulach Deleted all
* NotifyDescriptior constructors that take Icon as argument.
* 6 Gandalf 1.5 11/29/99 Petr Hamernik Adding elements using
* customizer
* 5 Gandalf 1.4 10/22/99 Ian Formanek NO SEMANTIC CHANGE - Sun
* Microsystems Copyright in File Comment
* 4 Gandalf 1.3 10/5/99 Petr Hamernik deadlock on Solaris fix
* 3 Gandalf 1.2 9/29/99 Petr Hamernik ClassCastException fixed
* 2 Gandalf 1.1 9/21/99 Petr Hamernik when entering new element
* - user is asked for name.
* 1 Gandalf 1.0 9/13/99 Petr Hamernik
* $
*/